/**	@file MouseTracer.h
 *
 *	Definition of CMouseTracer.h
 *
 *	@author JaeWook Choi
 *	@version 1.0
 *
 *  <b><i>This software is provided "as is" without express or implied warranty. Use it at your own risk!</i></b>
 *
 */

#if !defined(MOUSETRACER_H_INCLUDED)
#define MOUSETRACER_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "MessageHook.h"
#include "MGCfg.h"
#include <cmath>

#include "MGPattern.h"

// forward referecne
template<class TManager>
class CMouseTracer;

#include "MouseTracerManagerImpl5.h"
typedef CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl5> > MouseTracer;

template<class TManager>
class CMouseTracer : public CMessageHook<CMouseTracer>
{
	enum { TTIMER_ID = 12345 };
	
public:
	static BOOL Setup(HINSTANCE hMod = NULL, DWORD dwThreadID = 0);
	static void Remove(DWORD dwThreadID = 0);

public:
	CMouseTracer(BOOL bAutoDelete = FALSE, BOOL *pbSinkNotifyDeletion = NULL);
	virtual ~CMouseTracer();

public:
	BOOL GetVirtualKeyState(int nVirtualKey, WPARAM wParam = 0) const;
	BOOL IsTemporarySuspended(WPARAM wParam = 0) const;
	inline BOOL HasMouseCapture() const;
	inline void SetTargetCategory(BYTE bCatTarget) { m_bCatTarget = bCatTarget; }
	virtual BOOL ProcessWindowMessage(UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult);

	// Implementations
protected:
	
	BOOL OnButtonDblClk(enum MG::Button button, int x, int y);
	BOOL OnButtonDown(enum MG::Button button, int x, int y);
	BOOL OnButtonUp(enum MG::Button button, int x, int y);
	BOOL OnMouseWheel(int zDelta);
	BOOL CheckMGAButtonDown(UINT vkDown);
	BOOL OnMouseMove(int x, int y);
	BOOL OnCaptureChanged(HWND hWndGaining);

	BOOL ActivateMouseGesture(int x, int y);
	BOOL DoubleClickScrolling(int x, int y);
	UINT Distance(const POINT &ptOne, const POINT &rptTwo) const;
	UINT GetArrowDirection(const POINT &rptStart, const POINT &rptEnd) const;
	
	void SetHelpTipTimer();
	void KillHelpTipTimer();
	static void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
	class TimerProcThunk
	{
	public:
		
#if defined(_M_IX86)
		
#pragma pack(push, 1)
		struct _TimerProcThunk
		{
			DWORD   m_mov;          // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
			DWORD   m_this;         //
			BYTE    m_jmp;          // jmp WndProc
			DWORD   m_relproc;      // relative jmp
		};
#pragma pack(pop)
		
#else	// #if defined(_M_IX86)
		
#error Only X86 supported
		
#endif	// #if defined(_M_IX86)
		
		_TimerProcThunk thunk;
		
		void Init(TIMERPROC proc, void* pThis)
		{
			
#if defined(_M_IX86)
			
			thunk.m_mov = 0x042444C7;  // C7 44 24 04
			thunk.m_this = (DWORD)pThis;
			thunk.m_jmp = 0xe9;
			thunk.m_relproc = (int)proc - ((int)this + sizeof(_TimerProcThunk));
			
#endif	// #if defined(_M_IX86)
			
			// write block from data cache and flush from instruction cache
			::FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk));
		}
	} m_thunkTimer;

private:
	BOOL m_bWheelCmd;
	BOOL m_bTempPreemptive;
	BOOL m_bHasCapture;

	CMGPattern m_pattern;
	TCHAR m_tchMark;
	TCHAR m_tchMarkOld;

	DWORD m_dwMarkOldTime;

	WORD m_wCommand;

	POINT m_ptStart, m_ptEnd, m_ptTemp;

	BYTE m_bCatTarget;

	BOOL *m_pbSinkNotifyDeletion;

	BOOL m_bSetTimer;
};

#include "MouseTracer.hpp"

#endif // #if !defined(MOUSETRACER_H_INCLUDED)

